<?php
/**
 * Description of sintaxis
 *
 * @author tapion
 */
class sintaxis {

    private $frase = 0;         //Cuenta el numero de frases evaluadas por el compilador
    private $numToken = 0;      //Cuenta el numero del token en que se encuentra
    private $ind = 0;           //Indice del token que se esta evaluando dentro de la frase
    private $con;               //Variable para la conexion a la bd
    private $tipoDato = "";     //Tipo de dato que se declara en la variable
    private $tokenAnt = "";     //Ultimo token que se ha evaluado
    private $tokenAntVal = "";  //Valor del ultimo Token
    private $transaccion = "";   //almacena el numero de la transaccion que estamos manejando
    private $tokensSiguen;      //arreglo con los tokens que esperan continuar
    private $error = false;     //Bandera que controla los errores
    private $evaluado = "";     //Almacena todos los tokens que se ha evaluado
    private $palabras = "";     //Almacena todas las palabras que han sido evaluados
    private $Enoperacion = false; //Indica que esta en un proceso aritmetico
    private $iniOperacion = array("operaAgrupaParAbre", "operaAritmetico", "operaAsignacion");    //arreglo que contiene los tokens que inician una operacion
    private $finOperacion = array("operaAgrupaParCie", "coma", "termina");                                  //arreglo que contiene los tokens que finalizan una operacion
    private $ultVariable = "";  //Almacena la ultima variable
    private $tipoDatoEsperado = "";     //tipo de dato que se espera despues de realizar la operacion aritmetica
    private $tipoDatoVariable = "";     //tipo de dato de la ultima variable que se creo
    private $paraEvaluarOperacion = ""; //Almacenan todas las operaciones que se han realizado
    private $arregloTemporal = "";
    private $longitudArregloTmp = "";
    private $evalOperaciones = true;
    private $general = "";                      //variable de proposito general
    private $arregloBloques = array();      //Este arreglo contendra de forma ordenada el arbol de instrucciones que se esta compilando(los if y for)
    private $temporal = false;
    private $error_ = false;
    private $arrayFor = array("variable", "operaAsignacion", "numero", "variable", "operaCompara", "numero", "variable", "operaAsignacion1", "numero", "operaAgrupaParCie");
    private $isfor = false;
    private $arrayEstructura = array();     //array temporal para almacenar estructuras
    private $isPrintf = false;                      //bandera que indica que se trata de un printf
    private $arrayPrintf = array();         //array con los tipos de datos que va a representar
    private $isArray = false;               //bandera que indica si es un array
    private $tipoDatoArreglo = "";      //tipo de dato del arreglo
    private $tipoDatoArreglo1 = "";      //tipo de dato del arreglo
    private $fueArray = false;              //Indica si fue un array
    private $variableArray = "";            //almacena la ultima variable que se creo
    private $variableArray1 = "";            //almacena la ultima variable que se creo
    private $transaccionErrores = "";       //Transaccion para almacenar los errores cometidos
    private $unSeparador = true;         //Bandera para controlar los separadores de miles en el printf
    private $cuentaPrintf = 1;          //indica el valor que debe incrementar 
    private $tmpChar = false;       //Indica si se esta declarando o usando una variable tipo char
    private $tmpCharTemina = false; //bandera que indica si termina o no la conversion de un char
    private $tmpCharVal = "";       //almacena la funcion para convertir el char al numero ascii
    private $tmpFrase = "";         //variable para almacenar temporalmente todos los tokens que se van a evaluar en una frase
    private $enasignacion = false;  //variable que indica si esta en una asignacion
    public function analizar($compilado, $fuente, $transaccion, $transaccionPricipal, $compila) {
        /* Captura todo el programa compilado y el codigo fuente */
        $this->transaccionErrores = $transaccionPricipal;
        $this->transaccion = $transaccion;
        //elimina todo el contenido de que se almacene en la transaccion
        $sql = "delete from temporal where transaccion = '{$this->transaccion}'";
        $this->con->consulta($sql);
        $compilado = explode(";", $compilado);
        $fuente = explode(";", $fuente);
        array_pop($compilado);
        foreach ($compilado as $ind => $frase) {
            $this->tmpFrase = $frase;
            $this->analizaFrase(trim($frase, "$"), trim($fuente[$ind], "$"));
            if ($this->tipoDato != "") {
                $tmp = strrpos($this->palabras, $this->tipoDatoVariable);
                $tmp = substr($this->palabras, $tmp);
                $tmp2 = strrpos($this->evaluado, "tipoDato");
                $tmp2 = substr($this->evaluado, $tmp2);
                if(strrpos($tmp2, "coma")){
                    $tmp21 = str_replace('$coma', ";", $tmp2);
                    $compila->programa = str_replace($tmp2, $tmp21, $compila->programa);
                }
                //para asegurar que se altera unicamente la sentencia correcta se obtiene el substr hasta cuando exista un ;
                $tmp1 = strrpos($tmp, ';');
                if($tmp1 != "")$tmp = substr($tmp, $tmp1 +1);
                $tmp1 = str_replace('$,', ";", $tmp);
                $compila->compiladoPrograma = str_replace($tmp, $tmp1, $compila->compiladoPrograma); //remplaza ek cidigo fuente
            }

            if (!in_array("termina", $this->tokensSiguen)) {
                $this->publicaError("terminacion inesperada", __METHOD__, $palabra);
            }
            if ($this->error)
                break;
            if ($this->tmpCharTemina) {
                $this->tmpCharVal = "chr(" . $this->tmpCharVal . ")";
                $tmp = strrpos($this->palabras, "=");
                $tmpFuente = substr($this->palabras, $tmp);
                $tmp = strrpos($this->evaluado, "operaAsignacion");
                $tmpCompilado = substr($this->evaluado, $tmp);
                $compila->compiladoPrograma = str_replace($tmpFuente, "=$" . $this->tmpCharVal, $compila->compiladoPrograma); //remplaza ek cidigo fuente
                $compila->programa = str_replace($tmpCompilado, "operaAsignacion$" . "fnchar", $compila->programa); //remplaza ek cidigo fuente
            }
            $this->palabras = $this->palabras . ";";
        }
    }

//fin public function analizar($compilado, $fuente) {

    public function mostrar() {
        /* Funcion para saber si occurio un error en la validacion de la sintaxis */
        if ($this->error)
            return false;
        else
            return true;
    }

//fin public function mostrar(){

    private function analizaFrase($compilado, $fuente) {
        /* Funcion para evaluar cada frase que se ha compilado */
        $this->nuevaFrase();
        $compilado = explode("$", $compilado);
        $fuente = explode("$", $fuente);
        foreach ($compilado as $ind => $token) {
            $this->numToken++;
            $this->ind = $ind;
            $this->operacion($token, $fuente[$ind]);
            $this->evaluaPreanalisis($token);
            if ($this->error)
                break;
            if ($token == "operaAgrupaCorCie") {
                $this->general = end($this->arregloBloques);
                array_pop($this->arregloBloques);
                $tmp = array_slice($compilado, ($ind + 1));
                $tmp = $this->arrayTostring($tmp);
                $tmp1 = array_slice($fuente, ($ind + 1));
                $tmp1 = $this->arrayTostring($tmp1);
                $this->analizaFrase($tmp, $tmp1);
                array_push($this->tokensSiguen, "termina");
                if (!$this->error)
                    break;
            }
            if ($token == "operaAgrupaCorAbre" || $token == "operaDosPuntos") {
                if (count($this->arregloBloques) > 0) {
                    $tmp = array_slice($compilado, ($ind + 1));
                    $tmp = $this->arrayTostring($tmp);
                    $tmp1 = array_slice($fuente, ($ind + 1));
                    $tmp1 = $this->arrayTostring($tmp1);
                    $this->analizaFrase($tmp, $tmp1);
                    if (!$this->error)
                        break;
                }else{
                    if(!strrpos($this->tmpFrase,"operaInte"))
                        $this->publicaError("mal bloque", __METHOD__, $palabra, $token);
                }
            }
            if ($this->error)
                break;
            switch ($this->tokenAnt) {
                case "for":
                    $this->isfor = true;
                    break;
                case "printf":
                    $this->isPrintf = true;
                    break;
            }
            $this->analizaToken($token, $fuente[$ind]);
            if ($this->error)
                break;
        } //fin foreach ($compilado as $ind => $token) {
        if ($this->Enoperacion) {
            $this->operacion("termina");
        }
        return true;
    }

    private function evaluaPreanalisis($token) {
        /* Funcion para realizar evaluaciones previas a los tokens antes de evaluar su sintaxis */
        if ($this->isfor) { //si se trata de un ciclo for
            $alfabeto = current($this->arrayFor);
            switch ($alfabeto) {
                case "numero":
                    $tmp = "entero";
                    $alfabeto = "decimal";
                    break;
                case "operaAsignacion1":
                    $tmp = "operaAsignacion";
                    $alfabeto = "operaRecursivo";
                    break;
            }
            if (trim($alfabeto) != trim($token) && trim($tmp) != trim($token)) {
                $this->publicaError("mal for", __METHOD__, $token);
            } else {
                switch ($alfabeto) {
                    case "decimal":
                        $alfabeto = "numero";
                        break;
                    case "entero":
                        $alfabeto = "numero";
                        break;
                    case "operaRecursivo":
                        $alfabeto = "operaAsignacion1";
                        break;
                }
                array_push($this->arrayEstructura, $alfabeto);
                array_shift($this->arrayFor);
                if ($token == "operaRecursivo") {
                    array_shift($this->arrayFor);
                    array_push($this->arrayEstructura, "numero");
                }
                if ($token == "operaAgrupaParCie") {
                    $this->isfor = false;
                    $this->arrayFor = array("variable", "operaAsignacion", "numero", "variable", "operaCompara", "numero", "variable", "operaAsignacion1", "numero", "operaAgrupaParCie");
                }
            }
        } //fin if($this->isfor){
        //valida si es un array
        if ($token == "operaAgrupaCuadraAbre" && ($this->tokenAnt == "variable" || $this->tokenAnt == "operaAgrupaCuadraCie")) {
            $this->isArray = true;
            $this->variableArray = $this->ultVariable;
            array_push($this->tokensSiguen, "operaAgrupaCuadraAbre");
            array_push($this->tokensSiguen, "variable");
        }
        if ($token == "entero" && $this->tokenAnt == "operaAgrupaCuadraAbre" && $this->isArray) {
            array_push($this->tokensSiguen, "operaAgrupaCuadraCie");
            $this->tipoDatoArreglo = $this->tipoDatoVariable;
            $this->variableArray = $this->ultVariable;
        }

        if ($token == "operaAgrupaCuadraCie" && ($this->tokenAnt == "entero" || $this->tipoDatoVariable == "entero") && $this->isArray) {
            if ($this->tipoDato != "") {//esta creando el array
                $sql = "update temporal set tempInt1 = '{$this->tokenAntVal}', tempVarchar2 = 'array' where tempIndVar2 = '{$this->ultVariable}'  and transaccion = '{$this->transaccion}'";
                $this->con->consulta($sql);
            } else {
                $this->variableArray = ($this->variableArray1 != "") ? $this->variableArray1 : $this->variableArray;
                $sql = "select tempId from temporal where tempVarchar2 = 'array' and tempIndVar1 = 'variable' and tempIndVar2 = '{$this->variableArray}'  and transaccion = '{$this->transaccion}'";
                $result = $this->con->consulta($sql);
                if ($this->con->numeroFilas($result) <= 0) { //verifica que exista la variale como un array
                    $this->publicaError("la variable no es un array", __METHOD__, $this->variableArray, $token);
                    return true;
                } else {
                    //verifica que el indice del array no supere el maximo que ya existe
                    $indice = $this->tokenAntVal;
                    $sql = "select tempId from temporal where tempVarchar2 = 'array' and tempIndVar1 = 'variable' and tempIndVar2 = '{$this->variableArray}' and tempInt1 > $indice  and transaccion = '{$this->transaccion}'";
                    $result = $this->con->consulta($sql);
                    if ($this->con->numeroFilas($result) <= 0) { //verifica que exista la variale como un array
                        $this->publicaError("exeso indice", __METHOD__, $this->variableArray, $token);
                        return true;
                    }
                    if ($indice < 0) {
                        $this->publicaError("indice negativo", __METHOD__, $this->variableArray, $token);
                        return true;
                    }
                }
                $this->tipoDatoArreglo1 = $this->tipoDatoVariable;
                $this->variableArray1 = $this->variableArray;
            }
            $this->isArray = false;
            $this->fueArray = true;
        }
        return TRUE;
    }

// fin private function evaluaPreanalisis($token){

    private function arrayTostring($array) {
        /* Funcion para convertir un array en una cadena */
        $cadena = "";
        foreach ($array as $value) {
            $cadena .= "$" . $value;
        }
        $cadena = trim($cadena);
        $cadena = trim($cadena, "$");
        return $cadena;
    }

//fin private arrayTostring($array){

    private function operacion($token, $palabra = null) {
        if (!$this->evalOperaciones)
            return true;
        if (in_array($token, $this->iniOperacion) && $this->tokenAnt != "if") {
            if ($this->ultVariable == "") {
                $this->publicaError("sin destino", __METHOD__, $palabra);
                return true;
            } else {
                $this->tipoDatoEsperado = $this->tipoDatoVariable;
            }
            $this->Enoperacion = true;
        }
        if (in_array($token, $this->finOperacion) && $this->Enoperacion) {
            $this->arregloTemporal = explode("$", trim($this->paraEvaluarOperacion, "$"));
            $this->longitudArregloTmp = count($this->arregloTemporal);
            $this->tipoDatoVariable = ($this->fueArray) ? $this->tipoDatoArreglo1 : $this->tipoDatoVariable;
            $tmp = $this->obtResultado(0);
            if ($tmp == $this->tipoDatoVariable || ($tmp == "float" && $this->tipoDatoVariable == "entero"))
                $this->Enoperacion = false;
            else {
                $this->publicaError("resultado tipo dato incorrecto", __METHOD__, $palabra);
            }
        }
        return true;
    }

//fin private function operacion($token, $palabra = null) {

    private function obtResultado($posi) {
        /* Funcion para obtener el tipo de datos que se optiene despues de la operacion matematica */
        $tipoDato = "";
        $tmp = $this->arregloTemporal[$posi];
        if ($tmp == "(") {
            return $this->obtResultado($posi + 1);
        } else {
            //si es un operador sencillamente lo asigna y aumenta el contador
            if ($tmp == "+" || $tmp == "-" || $tmp == "/" || $tmp == "*" || $tmp == "%") {
                return $tmp;
            } elseif ($tmp == "entero" || $tmp == "float") {
                $tipoDatoOpera = $tmp;
                if (($posi + 1) < $this->longitudArregloTmp)
                    $operaOpera = $this->obtResultado($posi + 1);
                else
                    return $tmp;
                if (($posi + 2) < $this->longitudArregloTmp)
                    $tipoDato = $this->obtResultado($posi + 2);
                else
                    return $tmp;
                if ($tipoDato == "entero" && $tipoDatoOpera == "entero") {
                    if ($operaOpera == "+" || $operaOpera == "-" || $operaOpera == "%" || $operaOpera == "*")
                        return "entero";
                    elseif ($operaOpera == "/")
                        return "float";
                }elseif (($tipoDato == "entero" || $tipoDato == "float") && ($tipoDatoOpera == "entero" || $tipoDatoOpera == "float")) {
                    return "float";
                }
            }else
                return $tmp;
        }
    }

//fin private function obtResultado($pos) {

    private function muestraArray($array) {
        /* Muestra un array es una funcion para depurar */
        echo "<pre>";
        var_dump($array);
        echo "</pre>";
        return;
    }

// fin private function muestraArray($array){

    private function analizaToken($token, $palabra) {
        /* Realiza diferentes tipos de acciones de acuerdo al token establecido */
        if ($this->numToken == 1 || in_array($token, $this->tokensSiguen)) {
            switch ($token) {
                case 'tipoDato':
                    $sql = "select * from comandosc where comando = '$palabra'";
                    $result = $this->con->consulta($sql);
                    $result = $this->con->unaFila($result);
                    $this->tipoDato = $result["tipo"];
                    $this->siguientesTokens($token, $palabra);
                    break;
                case 'variable':
                    /*
                      tempIndVar1 = token(tipo de variable)
                      tempIndVar2 = valor del token(Nombre de la variable)
                      tempVarchar1 = tipo de datos de la variable
                      tempVarchar2 = indica si es un array
                     */
                    if ($this->tipoDato != "" && !$this->Enoperacion) { //esta en un proceso de declaracion de variables
                        $sql = "select * from temporal where tempIndVar1 = 'variable' and tempIndVar2 = '$palabra' and transaccion = '{$this->transaccion}'";
                        $result = $this->con->consulta($sql);
                        if ($this->con->numeroFilas($result) > 0) { //error
                            $this->publicaError("variable repetida", __METHOD__, $palabra);
                            return true;
                        } else {
                            $sql = "insert into temporal (transaccion,tempIndVar1,tempIndVar2,tempVarchar1) values(";
                            $sql .= "'{$this->transaccion}','$token','$palabra','{$this->tipoDato}')";
                            $this->con->consulta($sql);
                            $this->tipoDatoVariable = $this->tipoDato;
                            $this->siguientesTokens($token, $palabra, 'creando');
                            return true;
                        }
                    } else { //esta haciendo uso de la variable
                        //Se verifica que la variable exista
                        $sql = "select * from temporal where tempIndVar2 = '$palabra' and transaccion = '{$this->transaccion}'";
                        $result = $this->con->consulta($sql);
                        if ($this->con->numeroFilas($result) > 0) { //si existe la variable
                            $tipoDato = $this->con->unaFila($result);
                            $tipoDato = $tipoDato["tempVarchar1"];
                            if ($this->isArray) { //es un array se valida que la variable sea entera
                                if ($tipoDato != "entero") {
                                    $this->publicaError("indice en array entero", __METHOD__, $palabra);
                                    return true;
                                }
                                $this->tipoDatoArreglo = $this->tipoDatoVariable;
                                $this->variableArray = $this->ultVariable;
                            }
                        } else {
                            $this->publicaError("variable no existe", __METHOD__, $palabra);
                            return true;
                        }
                        if ($this->Enoperacion) { //esta asignando la variable, se verifica que los tipos de datos concuerden
                            $this->ultVariable = ($this->fueArray) ? $this->variableArray : $this->ultVariable;
                            if ($this->ultVariable == "") {
                                $this->publicaError("no se registro una variable antes", __METHOD__, $palabra);
                                return true;
                            } else {
                                $sql = "select * from temporal where tempIndVar2 = '{$this->ultVariable}' and transaccion = '{$this->transaccion}'";
                                $result = $this->con->consulta($sql);
                                if ($this->con->numeroFilas($result) > 0) {
                                    $result = $this->con->unaFila($result);
                                    if (($tipoDato != $result["tempVarchar1"]) && ($tipoDato == "entero" && $result["tempVarchar1"] != "decimal" )) {
                                        $this->publicaError("tipo de datos incorrecto", __METHOD__, $palabra);
                                        return true;
                                    }
                                } else {
                                    $this->publicaError("variable no existe", __METHOD__, $this->ultVariable);
                                    return true;
                                }
                            }
                        }
                        $this->tipoDatoVariable = $tipoDato;
                        switch ($tipoDato) {
                            case 'char':
                                $this->tmpChar = true;
                                $this->tmpCharVal = "ord($palabra)";
                                break;
                        }
                        $this->siguientesTokens($token, $palabra);
                        return true;
                    }
                    break;
                case 'coma':
                    $this->siguientesTokens($token, $palabra);
                    return true;
                    break;
                case 'operaAsignacion':
                    $this->enasignacion = true;
                    $this->tmpCharVal = "";
                    $this->siguientesTokens($token, $palabra);
                    return true;
                    break;
                case 'entero':
                    if ($this->tmpChar) {
                        $this->tmpCharVal .= $palabra;
                        $this->tmpCharTemina = true;
                    }
                    if ($this->tipoDato != "")
                        $this->validaTipoDato($token, $palabra);
                    else
                        $this->siguientesTokens($token, $palabra);
                    break;
                case 'string':
                    if ($this->tipoDato != "")
                        $this->validaTipoDato($token, $palabra);
                    else
                        $this->siguientesTokens($token, $palabra);
                    break;
                case 'char':
                    $this->tmpChar = true;
                    $this->tmpCharVal = "ord($palabra)";
                    if ($this->tipoDato != "")
                        $this->validaTipoDato($token, $palabra);
                    else
                        $this->siguientesTokens($token, $palabra);
                    break;
                case 'decimal':
                    if ($this->tipoDato != "")
                        $this->validaTipoDato($token, $palabra);
                    else
                        $this->siguientesTokens($token, $palabra);
                    break;
                case 'operaRecursivo':
                    if ($this->tokenAnt == "variable" && $this->tipoDatoVariable == "entero" && $this->tipoDato == "")
                        $this->siguientesTokens($token, $palabra);
                    else {
                        $this->publicaError("elemento inesperado", __METHOD__, $palabra);
                    }
                    break;
                default:
                    $this->siguientesTokens($token, $palabra);
                    break;
            }
        } else {
//            echo "es aqui $token $palabra <br/>";
            $this->publicaError("elemento inesperado", __METHOD__, $palabra, $this->tipoDato);
            return true;
        }
    }

    private function validaTipoDato($token, $palabra) {
        /* Funcion para validar si el tipo de datos es correcto */
        if ($this->tipoDato == $token || ($this->tipoDato == "decimal" && $token == "entero"))
            $this->siguientesTokens($token, $palabra);
        else { //el tipo de datos no concuerda
            $this->publicaError("tipo de datos incorrecto", __METHOD__, $palabra, $this->tipoDato);
        }
        return true;
    }

//fin private function validaTipoDato($token,$palabra){

    private function siguientesTokens($token, $palabra, $opc = null) {
        $this->tokenAntVal = $palabra;
        $sql = "select tks.token from tokens_conecciones tkcon
                inner join tokens tk on tk.tokenId = tkcon.tokenId
                inner join tokens tks on tks.tokenId = tkcon.tokenSigue
                where tk.token = '$token'";
        $result = $this->con->consulta($sql);
        $siguientes = array();
        $this->palabras = $this->palabras . "$" . $palabra;
        $this->evaluado = $this->evaluado . "$" . $token;
        //Agrega todos los tokens que podran seguir al token actual
        while ($row = $this->con->unaFila($result))
            array_push($siguientes, $row["token"]);
        //si se trata de la creacion de una variable se concatena manualmente la coma
        //define un comportamiento para cada token
        switch ($token) {
            case 'variable':
                $this->ultVariable = $palabra;
                if ($this->isPrintf) {
                    
                    $tmp1 = true;
                    $tmp = current($this->arrayPrintf);
                    if ($tmp == "ambos") {
                        if ($this->tipoDatoVariable == "entero" || $this->tipoDatoVariable == "decimal")
                            $tmp1 = false;
                    }
                    
                    if ($this->tipoDatoVariable != $tmp && count($this->arrayPrintf) > 0 && $tmp1 && !strrpos($this->tmpFrase,"operaInte") ) {
                        $this->publicaError("imprime tipo dato mal", __METHOD__, $palabra);
                        return;
                    } else {
                        array_shift($this->arrayPrintf);
                        array_push($siguientes, "coma");
                        array_push($siguientes, "operaInte");
                    }
                } //fin if($this->isPrintf){
                if ($this->tipoDato != "")
                    array_push($siguientes, "coma");
                //si esta en una operacion concatene el tipo de datos de la variable para su posterior analisis
                if ($this->Enoperacion)
                    $this->sumaOperacion($this->tipoDatoVariable);
                if ($this->isArray) {
                    $siguientes = array("operaAgrupaCuadraCie");
                }
                break;
            case 'operaAritmetico':
                if ($this->tmpChar)
                    $this->tmpCharVal .= $palabra;
                $this->sumaOperacion($palabra);
                break;
            case 'if':
                $this->sumaBloques("if");
                $this->evalOperaciones = false;
                break;
            case 'switch':
                $this->evalOperaciones = false;
                $this->sumaBloques("switch");
                break;
            case 'do':
                $this->sumaBloques("do");
                break;
            case 'while':
                $this->sumaBloques("while");
                $this->evalOperaciones = false;
                break;
            case 'for':
                $this->sumaBloques("for");
                $this->evalOperaciones = false;
                break;
            case 'coma':
                if ($this->isPrintf) {
                    array_push($siguientes, "entero");
                    array_push($siguientes, "decimal");
                    array_push($siguientes, "char");
                }
                break;
            case 'string':
                if ($this->isPrintf) {
                    $this->validaMensaje($palabra);
                    array_push($siguientes, "coma");
                }
                break;
            case 'printf':
                $this->evalOperaciones = false;
                break;
            case 'else':
                if ($this->general != "if")
                    $this->publicaError("else sin if", __METHOD__, $palabra, $this->tipoDato);
                array_push($this->arregloBloques, "else");
                break;
            case 'operaRecursivo':
                array_push($siguientes, "operaAgrupaParCie");
                break;
            case 'operaAgrupaCuadraAbre':
                array_push($siguientes, "variable");
                break;
            default :
                if ($token == "entero" || $token == "decimal" || $token == "char" || $token == "string") {
                    if($this->isPrintf || $this->enasignacion)
                        array_push($siguientes, "operaInte");
                    if ($this->tokenAnt == "case")
                        array_push($siguientes, "operaDosPuntos");
                }
                if ($this->Enoperacion)
                    $this->sumaOperacion($token);
                break;
        } //fin switch ($token){
        //condiciones para casos especiales
        if ($this->tipoDato != "") { //esta en un proceso de declaracion de variables
            array_push($siguientes, "coma");
        }
        $this->tokensSiguen = $siguientes;
        $this->tokenAnt = $token;
        return true;
    }

//fin private function siguientesTokens($token,$palabra,$opc = null){
    private function validaMensaje($cadena) {
        /* Funcion para validar que la cadena tenga los tipos de datos correctos */
        $this->unSeparador = true;
        $pos = stripos($cadena, "%");
        if ($pos === false) { //no encontro nada
            return;
        } else {
            $tipDato = strtolower($cadena[$pos + 1]);
            $this->fnTipoImprime($tipDato, $cadena, $pos);
            $str = substr($cadena, $pos + $this->cuentaPrintf);
            $this->validaMensaje($str);
            $this->cuentaPrintf = 1;
            $this->unSeparador = true;
        }
    }

//fin private function validaMensaje($cadena){

    private function fnTipoImprime($char, $cadena, $pos) {
        /* Funcion para identificar que tipo de dato se va a imprimir */
        $tipDato = strtolower($char);
        switch ($tipDato) {
            case 'd': //representa enteros
                array_push($this->arrayPrintf, "ambos");
                break;
            case 'e': //representa enteros
                array_push($this->arrayPrintf, "ambos");
                break;
            case 'g': //representa enteros
                array_push($this->arrayPrintf, "entero");
                break;
            case 'c': //representa char
                array_push($this->arrayPrintf, "char");
                break;
            case 'f': //representa float y double
                array_push($this->arrayPrintf, "decimal");
                break;
            case 'x': //representa floa y double
                array_push($this->arrayPrintf, "ambos");
                break;
            case 'o': //representa floa y double
                array_push($this->arrayPrintf, "ambos");
                break;
            default :
                $ascii = ord($tipDato);
                if (($ascii >= 48 && $ascii <= 57) || ($ascii == 46 && $this->unSeparador)) {
                    //valida que solo use un separador
                    $this->unSeparador = ($ascii == 46) ? FALSE : TRUE;
                    $this->cuentaPrintf++;
                    $pos = $pos + 1;
                    $tipDato = $cadena[$pos + 1];
                    $this->fnTipoImprime($tipDato, $cadena, $pos);
                } else {
                    $this->publicaError("uso porcentaje malo", __METHOD__, $tipDato);
                }
                break;
        }
        return;
    }

    private function sumaBloques($comando) {
        /* Funcion para sumar bloques de codigo */
        $primera = (count($this->arregloBloques) == 0) ? true : false;
        array_push($this->arregloBloques, $comando);
        if ($primera) {
            $this->general = current($this->arregloBloques);
        } else {
            next($this->arregloBloques);
            $this->general = current($this->arregloBloques);
        }
    }

    private function sumaOperacion($token) {
        /* funcion para concatenar tokens para la evaluacion de la operacion */
        switch ($token) {
            case "operaAgrupaParAbre":
                $token = "(";
                break;
            case "operaAsignacion":
                return;
                break;
            default:
                break;
        }
        $this->paraEvaluarOperacion .= "$" . $token;
        return;
    }

//fin private function sumaOperacion($token){

    private function publicaError($error, $metodo, $palabra, $token =null) {
        $this->error_ = true;
        $sql = "select errorMensaje from errores where errorCod = '$error'";
        $result = $this->con->consulta($sql);
        $result = $this->con->unaFila($result);
        //Si existe el mismo tipo de error lo elimina de la tabla temporal
        $sql = "delete from temporal where transaccion = '{$this->transaccionErrores}' and tempIndVar1 = '$error'";
        //Inserta el error en la temporal con la transaccion principal
        $sql = "insert into temporal (transaccion,tempIndVar1,tempIndVar2) values(";
        $sql .= "'{$this->transaccionErrores}','$error','errores')";
        $mensaje = "<div id='divMensajesCompila' class='tituloErrorCompila'>ERROR:  {$result["errorMensaje"]}</div>";
        $this->palabras = $this->palabras . "<span class='errorCompila'>" . $palabra . "</span>";
        //$this->compiladoPrograma = $this->compiladoPrograma . "$" . $this->compilado;
        $tmp = str_replace("$", " ", $this->palabras);
        $tmp = str_replace(";", ";<br/> ", $tmp);
        return true;
    }

    public function nuevaFrase() {
        /* Realiza todos los procesos necesarios para limpiar variables y datos cuando se trata de una nueva frase para evaluar */
        $this->frase++;
        $this->numToken = 0;
        $this->tipoDato = "";
        $this->isArray = false;
        $this->tipoDatoArreglo = "";
        $this->tipoDatoArreglo1 = "";
        $this->fueArray = false;
        $this->Enoperacion = false;
        $this->isPrintf = false;
        $this->variableArray = "";
        $this->variableArray1 = "";
        $this->paraEvaluarOperacion = "";
        $this->tmpChar = false;
        $this->tmpCharVal = "";
        $this->tmpCharTemina = false;
        $this->enasignacion = false;
        if (!$this->isfor) //recetea el calculo de operadores unicamente si no esta en un ciclo for
            $this->evalOperaciones = true;
        $this->temporal = false;
    }

    public function conexion($con) {
        //funcion para establecer localmente la instancia de la clase de conexion
        $this->con = $con;
    }

}

?>
